/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package controller.algorithme;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import model.*;

/**
 *
 * @author benoit
 */
public class AlgoTrajetLePlusCourt implements Algorithme {
    /* Constante de temps */
    private static int[] TEMPS_ETAGE = new int[5];
   
    static {
        TEMPS_ETAGE[0] = 0;
        TEMPS_ETAGE[1] = 3;
        TEMPS_ETAGE[2] = 6;
        TEMPS_ETAGE[3] = 8;
        TEMPS_ETAGE[4] = 10;
    }
    
    @Override
    public Ascenseur choisirAscenseur(AppelAscenseur appelAscenseur, Batiment batiment) {
        Ascenseur ascenseurChoisi=null;
        int directionAppel;
        int tempsAscenseurChoisi = 1000;
        int tempsAscenseur = 0;
                
        //Initialisation de la direction de l'appel
        directionAppel = getDirectionAppelAscenseur(appelAscenseur);

        for (Ascenseur ascenseur : batiment.getAscenseurs().values()) {
            if (!(ascenseur.getStatut() == Ascenseur.ST_BLOQUE || ascenseur.getStatut() == Ascenseur.ST_ARRET_D_URGENCE)) {
                if (ascenseurChoisi == null) {
                    ascenseurChoisi = ascenseur;
                }

                //Si l'ascenseur a des trajets en attente
                if (!ascenseur.getTrajetsEnCours().isEmpty()) {
                    //Si l'ascenseur va dans la même direction que l'appel passé
                    if (ascenseur.getDirectionActuel() == directionAppel) {
                        //Si l'appel est dans le trajet de l'ascenseur
                        if ((appelAscenseur.getEtageDepart().getNumero() > ascenseur.getEtageCourant().getNumero())) {
                            //Si on a le temps de s'arreter
                            if (getDiffEtage(appelAscenseur.getEtageDepart(), ascenseur.getEtageCourant()) >= 2) {
                                tempsAscenseur = calculerCoutMouvement(appelAscenseur, ascenseur, true);
                            }
                        }
                    }

                    //Si l'ascenseur ne peut pas s'arreter pour prendre l'utilisateur,
                    //on calcule le temps nécessaire pour finir le trajet, aller le chercher et le déposer
                    if (tempsAscenseur == 0) {
                        tempsAscenseur = calculerCoutMouvement(appelAscenseur, ascenseur, false);
                    }
                } else {
                    //Calcul du temps pour aller chercher un utilisateur quand l'ascenseur est à l'arret
                    tempsAscenseur = calculerCoutArret(appelAscenseur, ascenseur);
                }

                //Sélection de l'ascenseur si il est plus rapide
                if (tempsAscenseur < tempsAscenseurChoisi) {
                    tempsAscenseurChoisi = tempsAscenseur;
                    ascenseurChoisi = ascenseur;
                }
            }
        }

        return ascenseurChoisi;
    }
    
    private int calculerCoutArret(AppelAscenseur appelAscenseur, Ascenseur ascenseur)
    {
        int temps;
        
        //Calcul du temps que mettra l'ascenseur à rejoindre l'étage de départ de l'utilisateur
        temps = getTempsDiffEtage(ascenseur.getEtageCourant(), appelAscenseur.getEtageDepart());
        
        //Calcul du temps que mettra l'ascenseur à rejoindre l'étage d'arrivé de l'utilisateur
        temps = temps + getTempsDiffEtage(appelAscenseur.getEtageDepart(), appelAscenseur.getEtageArrive());
        
        return temps;
    }
    
    private int calculerCoutMouvement(AppelAscenseur appelAscenseur, Ascenseur ascenseur, boolean mode)
    {
        int temps = 0;
        int i;
        //int trajetAscenseur[] = null;
        List<Integer> trajetAscenseur = new ArrayList<>();
        
        //mode = true Si l'ascenseur peut s'arreter 
        if (mode == true) 
        {
            //Initialisation du tableau avec les étages connus
            trajetAscenseur.add(ascenseur.getEtageCourant().getNumero()); //Etage départ ascenseur
            trajetAscenseur.add(appelAscenseur.getEtageDepart().getNumero()); //Etage départ user
            trajetAscenseur.add(appelAscenseur.getEtageArrive().getNumero());//Etage arrivé user
            
            //récupération des étages avec arret entre l'ascenseur et l'utilisateur
            for(Trajet trajetEnAttente : ascenseur.getTrajetsEnAttente())
            {
                if(trajetEnAttente.getEtageInitial().getNumero() < appelAscenseur.getEtageArrive().getNumero())
                {
                    trajetAscenseur.add(trajetEnAttente.getEtageInitial().getNumero());
                }
                
                if(trajetEnAttente.getEtageArrive().getNumero() < appelAscenseur.getEtageArrive().getNumero())
                {
                    trajetAscenseur.add(trajetEnAttente.getEtageArrive().getNumero());
                }
                
                Collections.sort(trajetAscenseur);
            }
        }
        else
        {
            //Récupération des étages pour terminer les trajets en cours
            trajetAscenseur.add(ascenseur.getEtageCourant().getNumero());
            
            for(Trajet trajetEnAttente : ascenseur.getTrajetsEnAttente())
            {
                trajetAscenseur.add(trajetEnAttente.getEtageInitial().getNumero());
                trajetAscenseur.add(trajetEnAttente.getEtageArrive().getNumero());
            }
            Collections.sort(trajetAscenseur);
            
            //Ajout de l'étage de départ de l'utilisateur
            trajetAscenseur.add(appelAscenseur.getEtageDepart().getNumero());
            
            //Ajout de l'étage d'arrivé de l'utilisateur
            trajetAscenseur.add(appelAscenseur.getEtageArrive().getNumero());
        }

            //calcul du temps total
            for(i = 0 ; i <= trajetAscenseur.size() -1 ; i++)
            {
                temps = temps + getTempsDiffNumEtage(trajetAscenseur.get(i), trajetAscenseur.get(i));
            }
                
        return temps;
    }
    
    private int getDirectionAppelAscenseur(AppelAscenseur appelAscenseur)
    {
        int directionAppel;
        
        if((appelAscenseur.getEtageDepart().getNumero() - appelAscenseur.getEtageArrive().getNumero()) > 0)
        {
            directionAppel = Ascenseur.DESCENTE;
        }
        else
        {
            directionAppel = Ascenseur.MONTEE;
        }
        
        return directionAppel;
    }

    private int getTempsDiffEtage(Etage EtageDepartAscenseur, Etage etageArriveeAscenseur) 
    {
        int diffEtage, temps;
        
        diffEtage = getDiffEtage(EtageDepartAscenseur, etageArriveeAscenseur);
        
        if(diffEtage <= 4)
        {
            temps = TEMPS_ETAGE[diffEtage];
        }
        else
        {
            diffEtage = diffEtage - 4;
            temps = TEMPS_ETAGE[4] + diffEtage;
        }
        
        return temps;
    }
    
        private int getTempsDiffNumEtage(int EtageDepartAscenseur, int etageArriveeAscenseur) 
    {
        int diffEtage, temps;
        
        diffEtage = Math.abs(EtageDepartAscenseur - etageArriveeAscenseur);
        
        if(diffEtage <= 4)
        {
            temps = TEMPS_ETAGE[diffEtage];
        }
        else
        {
            diffEtage = diffEtage - 4;
            temps = TEMPS_ETAGE[4] + diffEtage;
        }
        
        return temps;
    }
    
    private int getDiffEtage (Etage EtageDepartAscenseur, Etage etageArriveeAscenseur) 
    {
        int diffEtage;
        
        diffEtage = Math.abs(EtageDepartAscenseur.getNumero() - etageArriveeAscenseur.getNumero());
        
        return diffEtage;
    }
}
